import 'dart:async';

import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:im_app/im/service/ImGroupService.dart';
import 'package:im_app/im/socket/service/RpcSendDataService.dart';
import 'package:im_app/im/util/ImCacheFactory.dart';
import 'package:im_app/im/util/ImSingleton.dart';
import 'package:im_app/im/util/UiUtil.dart';

import '../../dao/ImGroupDao.dart';
import '../../dao/ImGroupUserDao.dart';
import '../../dao/ImSessionDao.dart';
import '../../dao/ImUserDao.dart';
import '../../pojo/Enums.dart';
import '../../pojo/ImPojo.dart';
import '../../socket/dto/business/SendData.dart';
import '../../util/DbUtil.dart';
import 'BaseBloc.dart';
import 'SelectUsersCubit.dart';

MultiBlocProvider buildGroupCubit(ImSession? session, Widget widget) {
  var existUserIdSet = _buildExistedUserIdSet(session);
  return MultiBlocProvider(providers: [
    BlocProvider(
      create: (_) => GroupAddCubit(session),
    ),
    BlocProvider(
      create: (_) => SelectUsersCubit(existUserIdSet)..loadData(existUserIdSet),
    ),
  ], child: widget);
}

final class GroupAddCubit extends BaseBloc<GroupAddEvent, GroupAddState> {
  GroupAddCubit(ImSession? session) : super(GroupAddState.loading(session)) {
    on<GroupAddCancelEvent>((event, emit) {
      _cancel(state);
    });
    on<GroupAddSaveEvent>((event, emit) {
      _save(state.copy(selectedUsers: event.selectUsers));
    });
  }

  _cancel(GroupAddState state) {
    GroupAddState newState = state.copy(selectedUsers: <ImUser>[], selectedUserMap: <String, ImUser>{});
    emit(newState);
  }

  _save(GroupAddState state) async {
    var userId = ImSingleton.getInstance().session.userId!;

    var imGroupService = ImGroupService();
    var rpcGroupService = RpcGroupService();
    var sessionDao = ImSessionDao();

    if (state.selectedUsers.isEmpty) return;

    ImUser? toUser = null;
    ImGroup? imGroup = null;
    if (state.imSession != null) {
      toUser = state.imSession!.toUser;
      imGroup = state.imSession!.group;
    }

    var selectedUsers = state.selectedUsers;

    if (imGroup == null) {
      // 新建群 或 p2p聊天邀请人进群
      var currentUser = await ImCacheFactory.getInstance().getImUser(userId);
      if (!selectedUsers.contains(currentUser)) {
        selectedUsers.add(currentUser!);
      }

      if (toUser != null && !selectedUsers.contains(toUser)) {
        selectedUsers.add(toUser!);
      }
    }

    if (selectedUsers.length == 2 && imGroup == null) {
      // p2p聊天
      ImUser toUser = selectedUsers.firstWhere((u) => u.userId != userId);
      ImSession? imSession = await sessionDao.getByUserId(userId, userId, toUser.userId);
      if (imSession != null) {
        GroupAddState newState = state.copy(forwardRouter: "/MessageSendPage", forwardSession: imSession);
        emit(newState);
      } else {
        imSession = ImSession.buildWithUser(userId, toUser);
        sessionDao.insert(imSession);
        GroupAddState newState = state.copy(forwardRouter: "/MessageSendPage", forwardSession: imSession);
        emit(newState);
      }
    } else {
      // 群聊
      var imGroupDao = ImGroupDao();
      var imGroupUserDao = ImGroupUserDao();

      if (imGroup != null) {
        // 邀请用户进群
        var groupUsers = imGroupService.buildImGroupUsers(imGroup!.id!, selectedUsers);

        imGroup.groupUserList = groupUsers;
        await imGroupUserDao.batchInserts(imGroup!.id!, groupUsers);
        var newImGroup = await imGroupDao.getByCodeAndUserId(imGroup.code!, imGroup.currentUserId!);
        state.imSession!.group = newImGroup!;

        List<String> guIds = groupUsers.map((gu) => gu.userId).toList();
        var groupAddReq = GroupAddReq(groupId: imGroup.code, userIdSet: guIds, groupName: imGroup.name ?? '');

        var okResp = await rpcGroupService.groupAdd(groupAddReq);

        ImSingleton.getInstance().sessionListCubit?.changeSession(state.imSession!);

        GroupAddState newState = state.copy(forwardRouter: "/MessageSendPage", forwardSession: state.imSession);
        emit(newState);
      } else {
        // 建群
        ImSession imSession = imGroupService.buildImGroup(userId, selectedUsers);
        await imGroupService.createImGroup(imSession);
        await imGroupService.rpcCreateGroup(imSession);

        UiUtil.uiAddSession(imSession);

        GroupAddState newState = state.copy(forwardRouter: "/MessageSendPage", forwardSession: imSession);
        emit(newState);
      }
    }
  }

  Future<void> loadData() async {
    print('GroupAddCubit loadData!!!!!!!!!!');
    var userDao = ImUserDao();
    List<ImUser> users = await userDao.getUsers(10000);
    List<String> letters = _calacLetter(users);
    GroupAddState newState = GroupAddState.success(this.state.imSession, users, letters, _calacLetters(users));
    emit(newState);
  }

  @override
  Future<void> close() {
    return super.close();
  }

  List<String> _calacLetter(List<ImUser> users) {
    Set<String> letterSet = users.map((u) => u.firstLetter ?? '#').toSet();
    List<String> letters = letterSet.toList();
    letters.sort();
    return letters;
  }

  Map<String, int> _calacLetters(List<ImUser> users) {
    Map<String, int> map = {};
    for (var user in users) {
      var key = user.firstLetter ?? '#';
      if (map.containsKey(key)) {
        int value = map[key]!;
        map[key] = value + 1;
      } else {
        map[key] = 1;
      }
    }
    return map;
  }
}

final class GroupAddState extends Equatable {
  ImSession? imSession;
  final LoadingState state;
  final List<ImUser> selectedUsers;
  final Map<String, ImUser> selectedUserMap;
  final String search;
  final String forwardRouter;
  ImSession? forwardSession;

  GroupAddState._({
    this.state = LoadingState.loading,
    this.selectedUsers = const <ImUser>[],
    this.selectedUserMap = const <String, ImUser>{},
    this.search = '',
    this.forwardRouter = '',
    this.imSession,
    this.forwardSession,
  });

  GroupAddState copy(
      {ImSession? imSession,
      List<ImUser>? allUsers,
      List<ImUser>? selectedUsers,
      Map<String, ImUser>? selectedUserMap,
      List<ImUser>? showUsers,
      List<String>? letters,
      Map<String, int>? letterSizeMap,
      String? search,
      String? forwardRouter,
      ImSession? forwardSession}) {
    return GroupAddState._(
      imSession: imSession ?? this.imSession,
      selectedUsers: selectedUsers ?? this.selectedUsers,
      selectedUserMap: selectedUserMap ?? this.selectedUserMap,
      state: this.state,
      forwardRouter: forwardRouter ?? this.forwardRouter,
      forwardSession: forwardSession ?? this.forwardSession,
    );
  }

  GroupAddState.loading(ImSession? imSession) : this._(imSession: imSession);

  GroupAddState.success(
      ImSession? imSession, List<ImUser> allUsers, List<String> letters, Map<String, int> letterSizeMap)
      : this._(state: LoadingState.success, imSession: imSession);

  @override
  List<Object?> get props => [selectedUserMap, state, imSession, forwardSession, forwardRouter, search];
}

abstract class GroupAddEvent {}
final class GroupAddCancelEvent extends GroupAddEvent {
  GroupAddCancelEvent();
}
final class GroupAddSaveEvent extends GroupAddEvent {
  List<ImUser> selectUsers;
  GroupAddSaveEvent(this.selectUsers);
}


Set<String> _buildExistedUserIdSet(ImSession? session) {
  var result = new Set<String>();
  if (session != null) {
    if (session!.group != null) {
      session!.group!.groupUserList!.forEach((gu){
        result.add(gu.userId);
      });
    }
  }
  return result;
}